package org.jugile.util;

/*

Copyright (C) 2007-2011 Jukka Rahkonen  email: jukka.rahkonen@iki.fi

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/


import java.util.ArrayList;
import java.util.List;

/**
 * "For I am persuaded, that neither death, nor life, nor angels, nor principalities, 
 *  nor powers, nor things present, nor things to come, Nor height, nor depth, 
 *  nor any other creature, shall be able to separate us from the love of God, 
 *  which is in Christ Jesus our Lord." (Romans 8:38-39)
 * 
 * ==========
 * 
 * Simple utility for using csv data files. For some purposes simple and flat 
 * file with comma separated value rows is more convinient way to write
 * and read the data than corresponding xml or json structure.
 * 
 * @author jukka.rahkonen@iki.fi
 */
public class Csv extends Jugile {
	
	enum State {
		FLD,
		STR,
	}
	
	private int idx = 0;
	private List<List> rows = new ArrayList<List>();
	private List<String> headers = new ArrayList<String>();
	private char delimiter = ',';
	private boolean noHeaders = false;
	
	public Csv(String fname) {
		init(fname);
	}
	public Csv(String fname, char delimiter) {
		this.delimiter = delimiter;
		init(fname);
	}
	public Csv(char delimiter) {
		this.delimiter = delimiter;
	}
	
	public static Csv parseNoHeaders(String fname, char delimiter) {
		Csv c = new Csv(delimiter);
		c.noHeaders = true;
		c.init(fname);
		return c;
	}

	public int parse(String data) {
		if (empty(data)) fail("empty data");
		String lines[] = data.split("\r\n|\r|\n");
		if (lines.length < 1) return 0;
		headers = parseFields(lines[0]);
		for (int i = 1; i < lines.length; i++) {
			rows.add(parseFields(lines[i]));
		}		
		return rows.size();
	}
	
	private void init(String fname) {
		Blob b = new Blob(fname);
		String lines[] = b.getLines();
		if (lines.length < 1) return;
		if (!noHeaders)
			headers = parseFields(lines[0]);
		int max = 0;
		for (int i = 1; i < lines.length; i++) {
			List<String> flds = parseFields(lines[i]);
			if (max < flds.size()) max = flds.size();
			rows.add(flds);
		}
		if (noHeaders) {
			for (int i = 0; i < max; i++) {
				headers.add(""+(i+1));
			}
		}
	}

	public void setIdx(int i) { idx = i; }
	public int getIdx() { return idx; }
	public int size() { return rows.size(); }

	// TODO: implement hasNext() & next()  for easy while looping
	
	public String fld(int fld) { return fld(idx,fld); }
	public String fld(String fld) { return fld(idx,fld); }
	public String fld(int row, String fld) {
		int i = 0;
		for (String h : headers) {
			if (h.equals(fld)) break;
			i++;
		}
		return fld(row,i);
	}
	public String fld(int row, int fld) {
		List r = rows.get(row);
		if (fld >= r.size()) return null;
		return (String)r.get(fld);
	}

	public int cols() { return cols(idx); }
	public int cols(int row) {
		List r = rows.get(row);
		return r.size();
	}

	public String getHeader(int i) {
		return headers.get(i);
	}
	
	public List<String> parseFields(String line) {
		//print("line: "+ line);
		List<String> res = new ArrayList<String>();
		State st = State.FLD;
		StringBuffer fld = null;
		fld = new StringBuffer();
		for (int i = 0; i < line.length(); i++) {
			char ch = line.charAt(i);
			char peak1 = 0;
			if (i < line.length()-1) peak1 = line.charAt(i+1);
			//print("ch: " + ch);
			if (st == State.FLD) {
				if (ch == '"') {
					st = State.STR;
					continue;
				}
				if (ch == delimiter) {
					//print("fld:"+fld.toString());
					res.add(fld.toString());
					fld = new StringBuffer();
					continue;
				}
				fld.append(""+ch);
			}
			if (st == State.STR) {
				if (ch == '"') {
					if (peak1 == '"') {
						fld.append(""+peak1);
						i++;
						continue;
					} else {
						st = State.FLD;
						continue;
					}
				}
				if (ch == '\\') {
					if (peak1 == '"') {
						fld.append(""+peak1);
						i++;
						continue;
					} else if (peak1 == 'n') {
						fld.append("\n");
						i++;
						continue;
					} else if (peak1 == 't') {
						fld.append("\t");
						i++;
						continue;
					} else {
						fld.append(peak1);
						i++;
						continue;
					}
				}
				fld.append(""+ch);
			}
		}
		res.add(fld.toString());
		return res;
	}

	
	// ---------- scrap book ------------
	
	public static void main(String args[]) {
		Csv c = new Csv("data/classification.csv");
		for (List<String> line : c.rows) {
			print("line:");
			for (String fld : line) {
				print(" fld: " + fld);
			}
		}
		print("pick: " + c.fld(10, "type"));	
		//c.parseFields("\"hello \"\"jep\"\" man\", juuuu");
	}

}
